/*
 * Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
 *
 * This file is part of SeMoLearning.
 *
 * SeMoLearning is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SeMoLearning is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SeMoLearning.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef PARAMETERS_H_
#define PARAMETERS_H_

// default parameters
#include "semol/core/SemolExp.h" // DLL export/import defines
#include <string>
#include <map>

namespace semol
{

typedef std::map<std::string, std::string> ParametersMap; // Key, value
typedef std::pair<const std::string, std::string> ParametersPair;

/**
 * Macro used to create parameter's key and default value.
 * This macro must be used only in the Parameters class definition (in this file).
 * They are automatically added to the default parameters map of the class Parameters.
 * Example:
 * @code
 * 		//for PARAM(Video, ImageWidth, int, 640), the output will be :
 * 		public:
 * 			static std::string kVideoImageWidth() {return std::string("Video/ImageWidth");}
 * 			static int defaultVideoImageWidth() {return 640;}
 * 		private:
 * 			class DummyVideoImageWidth {
 * 			public:
 * 				DummyVideoImageWidth() {parameters_.insert(ParametersPair("Video/ImageWidth", "640"));}
 * 			};
 * 			DummyVideoImageWidth dummyVideoImageWidth;
 * @endcode
 */
#define SEMOL_PARAM(PREFIX, NAME, TYPE, DEFAULT_VALUE, DESCRIPTION) \
	public: \
		static std::string k##PREFIX##NAME() {return std::string(#PREFIX "/" #NAME);} \
		static TYPE default##PREFIX##NAME() {return DEFAULT_VALUE;} \
	private: \
		class Dummy##PREFIX##NAME { \
		public: \
			Dummy##PREFIX##NAME() {parameters_.insert(ParametersPair(#PREFIX "/" #NAME, #DEFAULT_VALUE)); \
								   descriptions_.insert(ParametersPair(#PREFIX "/" #NAME, DESCRIPTION));} \
		}; \
		Dummy##PREFIX##NAME dummy##PREFIX##NAME;
// end define PARAM

/**
 * It's the same as the macro PARAM but it should be used for string parameters.
 * Macro used to create parameter's key and default value.
 * This macro must be used only in the Parameters class definition (in this file).
 * They are automatically added to the default parameters map of the class Parameters.
 * Example:
 * @code
 * 		//for PARAM_STR(Video, TextFileName, "Hello_world"), the output will be :
 * 		public:
 * 			static std::string kVideoFileName() {return std::string("Video/FileName");}
 * 			static std::string defaultVideoFileName() {return "Hello_world";}
 * 		private:
 * 			class DummyVideoFileName {
 * 			public:
 * 				DummyVideoFileName() {parameters_.insert(ParametersPair("Video/FileName", "Hello_world"));}
 * 			};
 * 			DummyVideoFileName dummyVideoFileName;
 * @endcode
 */
#define SEMOL_PARAM_STR(PREFIX, NAME, DEFAULT_VALUE, DESCRIPTION) \
	public: \
		static std::string k##PREFIX##NAME() {return std::string(#PREFIX "/" #NAME);} \
		static std::string default##PREFIX##NAME() {return DEFAULT_VALUE;} \
	private: \
		class Dummy##PREFIX##NAME { \
		public: \
			Dummy##PREFIX##NAME() {parameters_.insert(ParametersPair(#PREFIX "/" #NAME, DEFAULT_VALUE)); \
								   descriptions_.insert(ParametersPair(#PREFIX "/" #NAME, DESCRIPTION));} \
		}; \
		Dummy##PREFIX##NAME dummy##PREFIX##NAME;
// end define PARAM

/**
 * Class Parameters.
 * This class is used to manage all custom parameters
 * we want in the application. It was designed to be very easy to add
 * a new parameter (just by adding one line of code).
 * The macro PARAM(PREFIX, NAME, TYPE, DEFAULT_VALUE) is
 * used to create a parameter in this class. A parameter can be accessed after by
 * Parameters::defaultPARAMETERNAME() for the default value, Parameters::kPARAMETERNAME for his key (parameter name).
 * The class provides also a general map containing all the parameter's key and
 * default value. This map can be accessed anywhere in the application by
 * Parameters::getDefaultParameters();
 * Example:
 * @code
 * 		//Defining a parameter in this class with the macro PARAM:
 * 		PARAM(Video, ImageWidth, int, 640);
 *
 * 		// Now from anywhere in the application (Parameters is a singleton)
 * 		int width = Parameters::defaultVideoImageWidth(); // theDefaultValue = 640
 * 		std::string theKey = Parameters::kVideoImageWidth(); // theKey = "Video/ImageWidth"
 * 		std::string strValue = Util::value(Parameters::getDefaultParameters(), theKey); // strValue = "640"
 * @endcode
 * @see getDefaultParameters()
 * TODO Add a detailed example with simple classes
 */
class SEMOL_EXP Parameters
{
	// Rtabmap parameters
	SEMOL_PARAM(Main, PublishStats, 	             bool, true, "Publishing statistics");
	SEMOL_PARAM(Main, PublishRawData, 	             bool, true, "Publishing raw data");
	SEMOL_PARAM(Main, PublishPdf, 	                 bool, true, "Publishing pdf");
	SEMOL_PARAM(Main, PublishLikelihood, 	         bool, true, "Publishing likelihood");
	SEMOL_PARAM(Main, RetrievalThr,                 float, 0.0, "Reactivation threshold");
	SEMOL_PARAM(Main, TimeThr, 		             float, 80.0, "Maximum time allowed for the detector (ms) (0 means infinity)");
	SEMOL_PARAM(Main, MemoryThr, 		             int, 0, "Maximum signatures in the Working Memory (ms) (0 means infinity)");
	SEMOL_PARAM(Main, SMStateBufferSize,            int, 0, "Data buffer size (0 min inf)");
	SEMOL_PARAM_STR(Main, WorkingDirectory, Parameters::getDefaultWorkingDirectory(), "Working directory");
	SEMOL_PARAM(Main, MaxRetrieved,                 unsigned int, 5, "Maximum locations retrieved at the same time from LTM");
	SEMOL_PARAM(Main, SelectionNeighborhoodSummationUsed, bool, false, "Neighborhood summation for hypothesis selection");
	SEMOL_PARAM(Main, SelectionLikelihoodUsed,      bool, false, "Neighborhood likelihood for hypothesis selection");
	SEMOL_PARAM(Main, ActionsSentRejectHyp,         bool, true, "Actions sent also on rejected hypotheses (on decreasing hypotheses)");
	SEMOL_PARAM(Main, ConfidenceThr, 		         float, 0.0, "Actions are not sent when the loop closure hypothesis is under the confidence threshold");
	SEMOL_PARAM(Main, LikelihoodStdDevRemoved,      bool, true, "Remove std dev on likelihood normalization.");
	SEMOL_PARAM(Main, LikelihoodNullValuesIgnored,  bool, false, "Ignore null values on likelihood normalization");
	SEMOL_PARAM(Main, NeighborhoodActionsSelection, unsigned int, 5, "Neighborhood depth for actions selection (0 means check only direct neighbors of the highest loop closure hypothesis).");

	// Hypotheses selection
	SEMOL_PARAM(Main, LoopThr,    	     float, 0.8, "Loop closing threshold");
	SEMOL_PARAM(Main, LoopRatio,    	 float, 0.0, "The loop closure hypothesis must be over LoopRatio x lastHypothesisValue");

	// Memory
	SEMOL_PARAM_STR(Mem, RehearsalMeans,      "1 1 1 1 1 1", "Rehearsal mean for each sensor [kTypeImage=0, kTypeAudio, kTypeAudioFreq, kTypeAudioFreqSqrdMagn, kTypeJointState, kTypeTwist, kTypeEnd]");
	SEMOL_PARAM_STR(Mem, RehearsalStds, 	    "0 0 0 0 0 0", "Rehearsal standard deviation for each sensor [kTypeImage=0, kTypeAudio, kTypeAudioFreq, kTypeAudioFreqSqrdMagn, kTypeJointState, kTypeTwist, kTypeEnd]");
	SEMOL_PARAM(Mem, RawDataKept, 		    bool, true, "Keep raw data");
	SEMOL_PARAM(Mem, MaxStMemSize, 		    unsigned int, 300, "Short-time memory size");
	SEMOL_PARAM(Mem, RecentWmRatio,           float, 0.2, "Ratio of locations after the last loop closure in WM that cannot be transferred");
	SEMOL_PARAM(Mem, AttentionDecreasingRate, float, 0.01, "percentage / iteration");
	SEMOL_PARAM(Mem, StmSizeById,             bool, true, "STM size by id (use id instead of number of nodes in STM)");

	// SM memory
	SEMOL_PARAM(SM, PublishSensorData, 	      bool, true, "Publishing sensor data");
	SEMOL_PARAM(SM, AudioDBThreshold,         float, 0.0, "Audio dB threshold");
	SEMOL_PARAM(SM, AudioLog,                 bool, true, "Audio Log spectrogram");
	SEMOL_PARAM(SM, AudioDBIndexing,          bool, true, "dB (decibel) indexing (otherwise it's squared magnitude indexing)");
	SEMOL_PARAM(SM, AudioIgnoredDCComponents, int, 1, "Ignore the first DC components (0=disabled)");
	SEMOL_PARAM(SM, AudioCrop,                float, 1.0, "Ratio of the frames used (cropping high frequencies)");
	SEMOL_PARAM(SM, AudioMinMagnitude,        float, 0.0, "Minimum Magnitude");
	SEMOL_PARAM(SM, TwistDecimalResolution,   int, 4, "Twist decimal resolution");
	SEMOL_PARAM(SM, ImageGrayScale,           bool, true, "Gray scale descriptor");

	//Database
	SEMOL_PARAM(Db, ImagesCompressed,    bool, true, "Images are compressed when save to database");
	SEMOL_PARAM(DbSqlite3, InMemory, 	   bool, false, "Using database in the memory instead of a file on the hard disk");
	SEMOL_PARAM(DbSqlite3, CacheSize,    unsigned int, 10000, "Sqlite cache size (default is 2000)");
	SEMOL_PARAM(DbSqlite3, JournalMode,  int, 3, "0=DELETE, 1=TRUNCATE, 2=PERSIST, 3=MEMORY, 4=OFF (see sqlite3 doc : \"PRAGMA journal_mode\")");
	SEMOL_PARAM(DbSqlite3, Synchronous,  int, 0, "0=OFF, 1=NORMAL, 2=FULL (see sqlite3 doc : \"PRAGMA synchronous\")");
	SEMOL_PARAM(DbSqlite3, TempStore,    int, 2, "0=DEFAULT, 1=FILE, 2=MEMORY (see sqlite3 doc : \"PRAGMA temp_store\")");

	// BayesFilter
	SEMOL_PARAM(Bayes, VirtualPlacePriorThr,           float, 0.9, "Virtual place prior");
	SEMOL_PARAM_STR(Bayes, PredictionLC, "0.1 0.3 0.44 0.08 0.04 0.02 0.02", "Prediction of loop closures (Gaussian-like, here with sigma=1.6) - Format: {VirtualPlaceProb, LoopClosureProb, NeighborLvl1, NeighborLvl2, ...}");
	SEMOL_PARAM(Bayes, PredictionOnNonNullActionsOnly, bool, false, "Make prediction on non-null action neighbors only");
	SEMOL_PARAM(Bayes, FullPredictionUpdate,           bool, false, "Regenerate all the prediction matrix on each iteration (otherwise only removed/added ids are updated).");
	SEMOL_PARAM(Bayes, Epsilon,           float, 0.00001, "Values under epsilon are not included in filter prior.");
	// Verify hypotheses
	SEMOL_PARAM(Vh,   Similarity,    float, 0.5, "Minimum similarity to accept an hypothesis");

public:
	virtual ~Parameters();

	/**
	 * Get default parameters of the current Profile if p is not set
	 *
	 * @see getProfile(), setProfile()
	 */
	static const ParametersMap & getDefaultParameters()
	{
		return parameters_;
	}

	/**
	 * Get parameter description
	 *
	 */
	static std::string getDescription(const std::string & paramKey);

	static void parse(const ParametersMap & parameters, const std::string & key, bool & value);
	static void parse(const ParametersMap & parameters, const std::string & key, int & value);
	static void parse(const ParametersMap & parameters, const std::string & key, unsigned int & value);
	static void parse(const ParametersMap & parameters, const std::string & key, float & value);
	static void parse(const ParametersMap & parameters, const std::string & key, double & value);
	static void parse(const ParametersMap & parameters, const std::string & key, std::string & value);

private:
	Parameters();
	static std::string getDefaultWorkingDirectory();

private:
	static ParametersMap parameters_;
	static ParametersMap descriptions_;
	static Parameters instance_;
};

}

#endif /* PARAMETERS_H_ */

